package com.miya.classloaddemo;

import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

import dalvik.system.PathClassLoader;

/**
 * 插件化hook技术的工具类
 */
public class HookUtils {

    public static final String HOOK_POINT_INTENT_KEY = "hook_point_intent_key";


    /**
     * hook起点。必须在Application中进行hook，否则效果不好。
     */
    public static void hook(Context context) {
        if (context instanceof Application) {
            hookAMS(context);
            hookHandler();
        }

    }

    /**
     * 对AMS的hook
     * 分为两个部分：
     * 1. intent送入AMS之前，把intent给替代成ProxyActivity的Intent
     * 2. intent从AMS出来之后，把intent替代成原始的Intent
     */
    public static void hookAMS(Context context) {
        //寻找Instrumentation中的execStartActivity，hook其中的IActivityManager
        try {
            Field singletonI;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Class<?> actManager = Class.forName("android.app.ActivityManager");
                singletonI = actManager.getDeclaredField("IActivityManagerSingleton");
            } else {
                Class<?> actManager = Class.forName("android.app.ActivityManagerNative");
                singletonI = actManager.getDeclaredField("gDefault");
            }
            singletonI.setAccessible(true);

            Object singleton = singletonI.get(null);
            if (singleton == null) {
                return;
            }
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            //获取IActivityManager的方法

            Class<?> singletonClass = Class.forName("android.util.Singleton");
            Field iActivityInstanceField = singletonClass.getDeclaredField("mInstance");
            iActivityInstanceField.setAccessible(true);
            final Object iActivityManager = iActivityInstanceField.get(singleton);

            Class<?> iActManagerClass = Class.forName("android.app.IActivityManager");
            //做一个代理，该动态代理指定该方法的
            Object iActivityManagerProxy = Proxy.newProxyInstance(loader, new Class[]{iActManagerClass}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    /**
                     * 方法startActivity没有重载
                     * startActivity(whoThread, who.getBasePackageName(), intent,
                     *                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                     *                         token, target != null ? target.mEmbeddedID : null,
                     *                         requestCode, 0, null, options);
                     *
                     */
                    if ("startActivity".equals(method.getName())) {
                        int index = -1;
                        for (int i = 0; i < args.length; i++) {
                            if (args[i] instanceof Intent) {
                                index = i;
                                break;
                            }
                        }

                        if (index >= 0) {
                            Intent oriIntent = (Intent) args[index];
                            //只做部分代理，即对指定的包名做代理
                            ComponentName componentName = oriIntent.getComponent();
                            if ("com.miya.plugin1.PluginActivity1".equals(componentName.getClassName())) {
                                Intent proxyIntent = new Intent();
                                proxyIntent.setClassName("com.miya.classloaddemo", "com.miya.classloaddemo.ProxyActivity");
                                proxyIntent.putExtra(HOOK_POINT_INTENT_KEY, oriIntent);
                                args[index] = proxyIntent;
                            }
                        }
                    }

                    return method.invoke(iActivityManager, args);
                }
            });
            iActivityInstanceField.set(singleton, iActivityManagerProxy);

        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 对从AMS回调后的启动点进行hook
     * 启动点为：ActivityThread->MH中的事件
     */
    public static void hookHandler() {
        try {
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Field instance = activityThreadClass.getDeclaredField("sCurrentActivityThread");
            instance.setAccessible(true);
            Object actThread = instance.get(null);//activityThread对象
            Field mhField = activityThreadClass.getDeclaredField("mH");
            mhField.setAccessible(true);
            Handler mh = (Handler) mhField.get(actThread);//获得mh的对象

            Handler.Callback callback = new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    try {
                        if (msg.what == 100) {//26版本及以下
                            //将ActivityClientRecord给拿出来，对Intent进行替换
                            Object activityClientRecord = msg.obj;
                            if (activityClientRecord == null) {
                                return false;
                            }
                            Field intentField = activityClientRecord.getClass().getDeclaredField("intent");
                            intentField.setAccessible(true);
                            Intent intent = (Intent) intentField.get(activityClientRecord);
                            Intent oriIntent = intent.getParcelableExtra(HookUtils.HOOK_POINT_INTENT_KEY);
                            if (oriIntent != null) {
                                //对intent进行替换
                                intentField.set(activityClientRecord, oriIntent);
                            }
                        } else if (msg.what == 159) {//28版本及以上
                            Object clientTransaction = msg.obj;
                            Field mActivityCallbacksField = clientTransaction.getClass().getDeclaredField("mActivityCallbacks");
                            mActivityCallbacksField.setAccessible(true);
                            List activityCallbacks = (List) mActivityCallbacksField.get(clientTransaction);
                            if (activityCallbacks == null || activityCallbacks.size() == 0) {
                                return false;
                            }
                            Object activityCallback = null;
                            for (int i = 0; i < activityCallbacks.size(); i++) {
                                activityCallback = activityCallbacks.get(i);
                                if ("android.app.servertransaction.LaunchActivityItem".equals(activityCallback.getClass().getName())) {
                                    Field innerIntentField = activityCallback.getClass().getDeclaredField("mIntent");
                                    innerIntentField.setAccessible(true);
                                    Intent mIntent = (Intent) innerIntentField.get(activityCallback);
                                    if (mIntent != null) {
                                        Intent oriIntent = mIntent.getParcelableExtra(HookUtils.HOOK_POINT_INTENT_KEY);
                                        if (oriIntent != null) {
                                            //对intent进行替换
                                            innerIntentField.set(activityCallback, oriIntent);
                                        }
                                    }
                                }
                            }
                        }

                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        e.printStackTrace();
                    }

                    return false;
                }
            };
            Field callbackField = Handler.class.getDeclaredField("mCallback");
            callbackField.setAccessible(true);
            callbackField.set(mh, callback);


        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}
